﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace ExpressionsDemo
{
    public static class Extensions
    {
        public static IQueryable<Customer> MyWhere(this IQueryable<Customer> customers)
        {
            ParameterExpression customerParam = Expression.Parameter(typeof(Customer), "c");
            MemberExpression titleProperty = Expression.Property(customerParam, "Title");

            Expression param1 = Expression.Constant("Mr.", typeof(string));
            Expression param3 = Expression.Constant("Ms.", typeof(string));

            Expression param2 = Expression.Constant(StringComparison.InvariantCultureIgnoreCase, typeof(StringComparison));


            MethodCallExpression equalMrExpr = Expression.Call(
                titleProperty,
                typeof(string).GetMethod("Equals", new Type [] { typeof(string), typeof(StringComparison) }),
                param1,
                param2);

            MethodCallExpression equalMrsExpr = Expression.Call(
                titleProperty,
                typeof(string).GetMethod("Equals", new Type [] { typeof(string), typeof(StringComparison) }),
                param3,
                param2);

            var body = Expression.Or(equalMrExpr, equalMrsExpr);
            var exprName = Expression.Lambda<Func<Customer, bool>>(body, customerParam);

            return customers.Where(exprName);
        }
    }


    class Program
    {
        static void Main(string [] args)
        {
            //Basic1();
            //Basic2();


            var pp = typeof(string).GetMethod("Equals", new Type [] { typeof(string), typeof(StringComparison) });

            AdventureWorksLT2008Entities ctx = new AdventureWorksLT2008Entities();

            //var customers1 = ctx.Customers
            //    .Where(c => c.Title.Equals("Mr.", StringComparison.InvariantCultureIgnoreCase)).ToList();

            IQueryable<Customer> queryableData = ctx.Customers.AsQueryable<Customer>();
            var result = queryableData.MyWhere().ToList();

            //var result = queryableData.

        }

        private static void Basic2()
        {
            string [] values = { "Aaa", "12345", "56789", "12345678" };
            var result = values.Where(x => x.ToLower() == "aaa" || x.Length == 5).OrderBy(x => x).AsQueryable();

            // prepare query
            IQueryable<string> queryableData = values.AsQueryable<string>();

            // compose tree
            System.Linq.Expressions.ParameterExpression pe = Expression.Parameter(typeof(string), "x");

            /* .Where(x => x.ToLower() == "aaa" || x.Length == 5) */
            // 1. x => x.ToLower() == "aaa"
            Expression left = Expression.Call(pe, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
            Expression right = Expression.Constant("aaa");
            Expression e1 = Expression.Equal(left, right);

            //x.Length == 5
            left = Expression.Property(pe, typeof(string).GetProperty("Length"));
            right = Expression.Constant(5, typeof(int));
            Expression e2 = Expression.Equal(left, right);

            // combine expressions
            Expression bodyExpr = Expression.OrElse(e1, e2);

            // where
            MethodCallExpression whereCallExpresion = Expression.Call(
                typeof(Queryable),
                "Where",
                new Type [] { queryableData.ElementType },
                queryableData.Expression,
                Expression.Lambda<Func<string, bool>>(bodyExpr, new ParameterExpression [] { pe }));

            // order by
            MethodCallExpression orderBy = Expression.Call(
                typeof(Queryable),
                "OrderBy",
                new Type [] { queryableData.ElementType, queryableData.ElementType },
                whereCallExpresion,
                Expression.Lambda<Func<string, string>>(pe, new ParameterExpression [] { pe }));

            var result2 = queryableData.Provider.CreateQuery<string>(orderBy);
            var result3 = queryableData.Provider.Execute<string>(orderBy);
        }

        private static void Basic1()
        {
            Expression<Func<int, bool>> lambda = num => num < 5;
            var expr = lambda.Compile();

            bool v1 = expr.Invoke(4);
            bool v2 = expr.Invoke(5);

            // build manually
            ParameterExpression numParam = Expression.Parameter(typeof(int), "num");

            System.Linq.Expressions.ConstantExpression five = Expression.Constant(5, typeof(int));
            System.Linq.Expressions.BinaryExpression numLessFive = Expression.Equal(numParam, five);

            Expression<Func<int, bool>> lambda1 =
                Expression.Lambda<Func<int, bool>>(numLessFive, new ParameterExpression [] { numParam });

            expr = lambda1.Compile();
            bool v3 = expr.Invoke(3);
        }
    }
}
